#include "Buf.h"

#include <string.h>
#include <fan-llvm/core/Core.h>
#include "FanInt.h"
#include "IndexErr.h"
#include "InStream.h"
#include "IOErr.h"
#include "OutStream.h"
#include "Range.h"
#include "Str.h"
#include "Type.h"
#include "UnsupportedErr.h"


static int8_t kHexInv[128];
static int8_t kBase64Inv[128];

static void Buf_setPos(Buf* self, Int pos);

Type* Buf_classType = NULL;
VTable* Buf_classVTable = NULL;


/**********************************************
 *
 * Methods and fields
 *
 **********************************************/


/* Bool equals(Obj? that) */
Bool Buf_equals(Obj self, NullableObj that)
{
  return (VAL_GET_REF(self) == VAL_GET_REF(that));
}


/* Str toStr() */
Str* Buf_toStr(Obj self)
{
  Buf* buf = VAL_GET_REF(self);
  return Str_newWithFormat("%r(pos=%i size=%i)", Type_name(Obj_typeof(self)), Buf_pos(buf), Buf_size(buf));
}


/* Int size */
Int Buf_size(Buf* self)
{
  return BUF_VTABLE(self)->size(self);
}


/* Int size */
void Buf_setSize(Buf* self, Int val)
{
  BUF_VTABLE(self)->setSize(self, val);
}


/* Int capacity */
Int Buf_capacity(Buf* self)
{
  return BUF_VTABLE(self)->capacity(self);
}


/* Int capacity */
void Buf_setCapacity(Buf* self, Int val)
{
  BUF_VTABLE(self)->setCapacity(self, val);
}


/* Endian endian */
Endian* Buf_endian(Buf* self)
{
  return OutStream_endian(self->out);
}


/* Endian endian */
void Buf_setEndian(Buf* self, Endian* val)
{
  OutStream_setEndian(self->out, val);
  InStream_setEndian(self->in, val);
}


/* Charset charset */
Charset* Buf_charset(Buf* self)
{
  return OutStream_charset(self->out);
}


/* Charset charset */
void Buf_setCharset(Buf* self, Charset* val)
{
  OutStream_setCharset(self->out, val);
  InStream_setCharset(self->in, val);
}


/* Buf make(Int capacity) */
Buf* Buf_make_0(void)
{
  return Buf_make(1024);
}


/* Buf make(Int capacity) */
Buf* Buf_make(Int capacity)
{
  return (Buf*)MemBuf_newEmpty(capacity);
}


/* Buf random(Int size) */
Buf* Buf_random(Int size)
{
  uint8_t* buf = memoryAlloc(size);

  for (Int i = 0; i < size; i += sizeof(Int)) {
    Int x = Int_random_0();
    if (i + sizeof(x) <= size)
      memcpy(buf + i, &x, sizeof(x));
    else
      memcpy(buf + i, &x, size - i);
  }

  return (Buf*)MemBuf_newFromBuffer(buf, size);
}


/* Bool isEmpty() */
Bool Buf_isEmpty(Buf* self)
{
  return (Buf_size(self) == 0);
}


/* Int pos() */
Int Buf_pos(Buf* self)
{
  return BUF_VTABLE(self)->pos(self);
}


/* [private] void setPos(Int pos) */
static void Buf_setPos(Buf* self, Int pos) {
  BUF_VTABLE(self)->setPos(self, pos);
}


/* Int remaining() */
Int Buf_remaining(Buf* self)
{
  return Buf_size(self) - Buf_pos(self);
}


/* Bool more() */
Bool Buf_more(Buf* self)
{
  return ((Buf_size(self) - Buf_pos(self)) > 0);
}


/* This seek(Int pos) */
Buf* Buf_seek(Buf* self, Int pos)
{
  Int size = Buf_size(self);
  if (pos < 0)
    pos = pos + size;
  if (pos < 0 || pos > size)
    exceptionThrow(IndexErr_newWithIndex(pos));
  Buf_setPos(self, pos);
  return self;
}


/* This flip() */
Buf* Buf_flip(Buf* self)
{
  Buf_setSize(self, Buf_pos(self));
  Buf_setPos(self, 0);
  return self;
}


/* Int get(Int index) */
Int Buf_get(Buf* self, Int index)
{
  Int size = Buf_size(self);
  if (index < 0)
    index = index + size;
  if (index < 0 || index >= size)
    exceptionThrow(IndexErr_newWithIndex(index));
  return BUF_VTABLE(self)->getByte(self, index);
}


/* Buf getRange(Range range) */
Buf* Buf_getRange(Buf* self, Range* range)
{
  Int size = Buf_size(self);
  Int s = Range_absStart(range, size);
  Int e = Range_absEnd(range, size);
  Int n = e - s + 1;
  if (n < 0)
    exceptionThrow(IndexErr_newWithRange(range));

  uint8_t* slice = memoryAlloc(n);
  BUF_VTABLE(self)->getBytes(self, s, slice, n);

  Buf* result = (Buf*)MemBuf_newFromBuffer(slice, n);
  Buf_setCharset(result, Buf_charset(self));
  return result;
}


/* Buf dup() */
Buf* Buf_dup(Buf* self)
{
  Int size = Buf_size(self);
  uint8_t* copy = memoryAlloc(size);
  BUF_VTABLE(self)->getBytes(self, 0, copy, size);

  Buf* result = (Buf*)MemBuf_newFromBuffer(copy, size);
  Buf_setCharset(result, Buf_charset(self));
  return result;
}


/* This set(Int index, Int byte) */
Buf* Buf_set(Buf* self, Int index, Int byte)
{
  Int size = Buf_size(self);
  if (index < 0)
    index = index + size;
  if (index < 0 || index >= size)
    exceptionThrow(IndexErr_newWithIndex(index));
  BUF_VTABLE(self)->setByte(self, index, byte);
  return self;
}


/* This clear() */
Buf* Buf_clear(Buf* self)
{
  Buf_setPos(self, 0);
  Buf_setSize(self, 0);
  return self;
}


/* This trim() */
Buf* Buf_trim(Buf* self)
{
  return self;
}


/* Bool close() */
Bool Buf_close(Buf* self)
{
  return TRUE;
}


/* This flush() */
Buf* Buf_flush(Buf* self)
{
  return self;
}


/* This fill(Int byte, Int times) */
Buf* Buf_fill(Buf* self, Int byte, Int times)
{
  Int size = Buf_size(self);
  if (Buf_capacity(self) < size + times)
    Buf_setCapacity(self, size + times);
  for (Int i = 0; i < times; i++)
    OutStream_write(self->out, byte);
  return self;
}


/* OutStream out() */
OutStream* Buf_out(Buf* self)
{
  return self->out;
}


/* This write(Int byte) */
Buf* Buf_write(Buf* self, Int byte)
{
  OutStream_write(self->out, byte);
  return self;
}


/* This writeBuf(Buf buf, Int n) */
Buf* Buf_writeBuf_1(Buf* self, Buf* buf)
{
  OutStream_writeBuf_1(self->out, buf);
  return self;
}


/* This writeBuf(Buf buf, Int n) */
Buf* Buf_writeBuf(Buf* self, Buf* buf, Int n)
{
  OutStream_writeBuf(self->out, buf, n);
  return self;
}


/* This writeI2(Int n) */
Buf* Buf_writeI2(Buf* self, Int n)
{
  OutStream_writeI2(self->out, n);
  return self;
}


/* This writeI4(Int n) */
Buf* Buf_writeI4(Buf* self, Int n)
{
  OutStream_writeI4(self->out, n);
  return self;
}


/* This writeI8(Int n) */
Buf* Buf_writeI8(Buf* self, Int n)
{
  OutStream_writeI8(self->out, n);
  return self;
}


/* This writeF4(Float r) */
Buf* Buf_writeF4(Buf* self, Float r)
{
  OutStream_writeF4(self->out, r);
  return self;
}


/* This writeF8(Float r) */
Buf* Buf_writeF8(Buf* self, Float r)
{
  OutStream_writeF8(self->out, r);
  return self;
}


/* This writeDecimal(Decimal d) */
Buf* Buf_writeDecimal(Buf* self, Decimal* d)
{
  OutStream_writeDecimal(self->out, d);
  return self;
}


/* This writeBool(Bool b) */
Buf* Buf_writeBool(Buf* self, Bool b)
{
  OutStream_writeBool(self->out, b);
  return self;
}


/* This writeUtf(Str s) */
Buf* Buf_writeUtf(Buf* self, Str* s)
{
  OutStream_writeUtf(self->out, s);
  return self;
}


/* This writeChar(Int char) */
Buf* Buf_writeChar(Buf* self, Int ch)
{
  OutStream_writeChar(self->out, ch);
  return self;
}


/* This writeChars(Str str, Int off, Int len) */
Buf* Buf_writeChars_1(Buf* self, Str* str)
{
  OutStream_writeChars_1(self->out, str);
  return self;
}


/* This writeChars(Str str, Int off, Int len) */
Buf* Buf_writeChars_2(Buf* self, Str* str, Int off)
{
  OutStream_writeChars_2(self->out, str, off);
  return self;
}


/* This writeChars(Str str, Int off, Int len) */
Buf* Buf_writeChars(Buf* self, Str* str, Int off, Int len)
{
  OutStream_writeChars(self->out, str, off, len);
  return self;
}


/* This print(Obj? s) */
Buf* Buf_print(Buf* self, NullableObj s)
{
  OutStream_print(self->out, s);
  return self;
}


/* This printLine(Obj? obj) */
Buf* Buf_printLine_0(Buf* self)
{
  OutStream_printLine_0(self->out);
  return self;
}


/* This printLine(Obj? obj) */
Buf* Buf_printLine(Buf* self, NullableObj obj)
{
  OutStream_printLine(self->out, obj);
  return self;
}


/* This writeProps([Str:Str] props) */
Buf* Buf_writeProps(Buf* self, Map* props)
{
  OutStream_writeProps_1(self->out, props);
  return self;
}


/* This writeObj(Obj? obj, [Str:Obj]? options) */
Buf* Buf_writeObj_1(Buf* self, NullableObj obj)
{
  OutStream_writeObj_1(self->out, obj);
  return self;
}


/* This writeObj(Obj? obj, [Str:Obj]? options) */
Buf* Buf_writeObj(Buf* self, NullableObj obj, Map* options)
{
  OutStream_writeObj(self->out, obj, options);
  return self;
}


/* This writeXml(Str s, Int flags) */
Buf* Buf_writeXml_1(Buf* self, Str* s)
{
  OutStream_writeXml_1(self->out, s);
  return self;
}


/* This writeXml(Str s, Int flags) */
Buf* Buf_writeXml(Buf* self, Str* s, Int flags)
{
  OutStream_writeXml(self->out, s, flags);
  return self;
}


/* InStream in() */
InStream* Buf_in(Buf* self)
{
  return self->in;
}


/* Int? read() */
NullableInt Buf_read(Buf* self)
{
  return InStream_read(self->in);
}


/* Int? readBuf(Buf buf, Int n) */
NullableInt Buf_readBuf(Buf* self, Buf* buf, Int n)
{
  return InStream_readBuf(self->in, buf, n);
}


/* This unread(Int b) */
Buf* Buf_unread(Buf* self, Int b)
{
  InStream_unread(self->in, b);
  return self;
}


/* Buf readAllBuf() */
Buf* Buf_readAllBuf(Buf* self)
{
  return InStream_readAllBuf(self->in);
}


/* Buf readBufFully(Buf? buf, Int n) */
Buf* Buf_readBufFully(Buf* self, Buf* buf, Int n)
{
  return InStream_readBufFully(self->in, buf, n);
}


/* Int? peek() */
NullableInt Buf_peek(Buf* self)
{
  return InStream_peek(self->in);
}


/* Int readU1() */
Int Buf_readU1(Buf* self)
{
  return InStream_readU1(self->in);
}


/* Int readS1() */
Int Buf_readS1(Buf* self)
{
  return InStream_readS1(self->in);
}


/* Int readU2() */
Int Buf_readU2(Buf* self)
{
  return InStream_readU2(self->in);
}


/* Int readS2() */
Int Buf_readS2(Buf* self)
{
  return InStream_readS2(self->in);
}


/* Int readU4() */
Int Buf_readU4(Buf* self)
{
  return InStream_readU4(self->in);
}


/* Int readS4() */
Int Buf_readS4(Buf* self)
{
  return InStream_readS4(self->in);
}


/* Int readS8() */
Int Buf_readS8(Buf* self)
{
  return InStream_readS8(self->in);
}


/* Float readF4() */
Float Buf_readF4(Buf* self)
{
  return InStream_readF4(self->in);
}


/* Float readF8() */
Float Buf_readF8(Buf* self)
{
  return InStream_readF8(self->in);
}


/* Decimal readDecimal() */
Decimal* Buf_readDecimal(Buf* self)
{
  return InStream_readDecimal(self->in);
}


/* Bool readBool() */
Bool Buf_readBool(Buf* self)
{
  return InStream_readBool(self->in);
}


/* Str readUtf() */
Str* Buf_readUtf(Buf* self)
{
  return InStream_readUtf(self->in);
}


/* Int? readChar() */
NullableInt Buf_readChar(Buf* self)
{
  return InStream_readChar(self->in);
}


/* This unreadChar(Int b) */
Buf* Buf_unreadChar(Buf* self, Int b)
{
  InStream_unreadChar(self->in, b);
  return self;
}


/* Int? peekChar() */
NullableInt Buf_peekChar(Buf* self)
{
  return InStream_peekChar(self->in);
}


/* Str readChars(Int n) */
Str* Buf_readChars(Buf* self, Int n)
{
  return InStream_readChars(self->in, n);
}


/* Str? readLine(Int? max) */
Str* Buf_readLine_0(Buf* self)
{
  return InStream_readLine_0(self->in);
}


/* Str? readLine(Int? max) */
Str* Buf_readLine(Buf* self, NullableInt max)
{
  return InStream_readLine(self->in, max);
}


/* Str? readStrToken(Int? max, |Int->Bool|? c) */
Str* Buf_readStrToken_0(Buf* self)
{
  return InStream_readStrToken_0(self->in);
}


/* Str? readStrToken(Int? max, |Int->Bool|? c) */
Str* Buf_readStrToken_1(Buf* self, NullableInt max)
{
  return InStream_readStrToken_1(self->in, max);
}


/* Str? readStrToken(Int? max, |Int->Bool|? c) */
Str* Buf_readStrToken(Buf* self, NullableInt max, Func* c)
{
  return InStream_readStrToken(self->in, max, c);
}


/* Str[] readAllLines() */
List* Buf_readAllLines(Buf* self)
{
  return InStream_readAllLines(self->in);
}


/* Void eachLine(|Str->Void| f) */
void Buf_eachLine(Buf* self, Func* f)
{
  return InStream_eachLine(self->in, f);
}


/* Str readAllStr(Bool normalizeNewlines) */
Str* Buf_readAllStr_0(Buf* self)
{
  return InStream_readAllStr_0(self->in);
}


/* Str readAllStr(Bool normalizeNewlines) */
Str* Buf_readAllStr(Buf* self, Bool normalizeNewlines)
{
  return InStream_readAllStr(self->in, normalizeNewlines);
}


/* [Str:Str] readProps() */
Map* Buf_readProps(Buf* self)
{
  return InStream_readProps(self->in);
}


/* Obj? readObj([Str:Obj]? options) */
NullableObj Buf_readObj_0(Buf* self)
{
  return InStream_readObj_0(self->in);
}


/* Obj? readObj([Str:Obj]? options) */
NullableObj Buf_readObj(Buf* self, Map* options)
{
  return InStream_readObj(self->in, options);
}


/* Str toHex() */
Str* Buf_toHex(Buf* self)
{
  return BUF_VTABLE(self)->toHex(self);
}


/* Buf fromHex(Str s) */
Buf* Buf_fromHex(Str* s)
{
  Int slen = s->len;
  uint8_t* buf = memoryAlloc(slen / 2);
  Int size = 0;

  for(Int i = 0; i < slen; i++) {
    Char c0 = s->buffer[i];
    int8_t n0 = (c0 < 128) ? kHexInv[c0] : -1;
    if (n0 < 0)
      continue;

    int8_t n1 = -1;
    if (++i < slen)
    {
      Char c1 = s->buffer[i];
      n1 = (c1 < 128) ? kHexInv[c1] : -1;
    }
    if (n1 < 0)
      exceptionThrow(IOErr_newWithFormattedMsg("Invalid hex str"));

    buf[size++] = (uint8_t)((n0 << 4) | n1);
  }

  return (Buf*)MemBuf_newFromBuffer(buf, size);
}


/* Str toBase64() */
Str* Buf_toBase64(Buf* self)
{
  return BUF_VTABLE(self)->toBase64(self);
}


/* Buf fromBase64(Str s) */
Buf* Buf_fromBase64(Str* s)
{
  Int slen = s->len;
  Int si = 0;
  Int max = (slen * 6) / 8;
  uint8_t* buf = memoryAlloc(max);
  Int size = 0;

  while (si < slen)
  {
    Int n = 0;
    Int v = 0;
    for (Int j = 0; j < 4 && si < slen; ) {
      Char ch = s->buffer[si++];
      int8_t c = (ch < 128) ? kBase64Inv[ch] : -1;
      if (c >= 0) {
        n |= c << (18 - j++ * 6);
        if (ch != '=')
          v++;
      }
    }
    if (v > 1)
      buf[size++] = (uint8_t)(n >> 16);
    if (v > 2)
      buf[size++] = (uint8_t)(n >> 8);
    if (v > 3)
      buf[size++] = (uint8_t)n;
  }

  return (Buf*)MemBuf_newFromBuffer(buf, size);
}


/* Buf toDigest(Str algorithm) */
Buf* Buf_toDigest(Buf* self, Str* algorithm)
{
  exceptionThrow(UnsupportedErr_newWithFormattedMsg("sys::Buf.toDigest not implemented yet"));
  return NULL;
}


/* Buf hmac(Str algorithm, Buf key) */
Buf* Buf_hmac(Buf* self, Str* algorithm, Buf* key)
{
  exceptionThrow(UnsupportedErr_newWithFormattedMsg("sys::Buf.hmac not implemented yet"));
  return NULL;
}


/**********************************************
 *
 * Type-system functions
 *
 **********************************************/


Type* Buf_initType(void)
{
  if (Buf_classType == NULL)
  {
    Type* baseType = Obj_initType();
    VTable* vtable = Buf_initVTable();
    Type* type = Type_new("Buf", baseType, vtable, sizeof(Buf), sizeof(BufVTable));
    Type_registerMethodImplementation(type, "clear", Buf_clear, 0);
    Type_registerMethodImplementation(type, "close", Buf_close, 0);
    Type_registerMethodImplementation(type, "dup", Buf_dup, 0);
    Type_registerMethodImplementation(type, "eachLine", Buf_eachLine, 1);
    Type_registerMethodImplementation(type, "equals", Buf_equals, 1);
    Type_registerMethodImplementation(type, "fill", Buf_fill, 2);
    Type_registerMethodImplementation(type, "flip", Buf_flip, 0);
    Type_registerMethodImplementation(type, "flush", Buf_flush, 0);
    Type_registerMethodImplementation(type, "fromBase64", Buf_fromBase64, 1);
    Type_registerMethodImplementation(type, "fromHex", Buf_fromHex, 1);
    Type_registerMethodImplementation(type, "get", Buf_get, 1);
    Type_registerMethodImplementation(type, "getRange", Buf_getRange, 1);
    Type_registerMethodImplementation(type, "hmac", Buf_hmac, 2);
    Type_registerMethodImplementation(type, "in", Buf_in, 0);
    Type_registerMethodImplementation(type, "isEmpty", Buf_isEmpty, 0);
    Type_registerMethodImplementation(type, "make", Buf_make_0, 0);
    Type_registerMethodImplementation(type, "make", Buf_make, 1);
    Type_registerMethodImplementation(type, "more", Buf_more, 0);
    Type_registerMethodImplementation(type, "out", Buf_out, 0);
    Type_registerMethodImplementation(type, "peek", Buf_peek, 0);
    Type_registerMethodImplementation(type, "peekChar", Buf_peekChar, 0);
    Type_registerMethodImplementation(type, "pos", Buf_pos, 0);
    Type_registerMethodImplementation(type, "print", Buf_print, 1);
    Type_registerMethodImplementation(type, "printLine", Buf_printLine_0, 0);
    Type_registerMethodImplementation(type, "printLine", Buf_printLine, 1);
    Type_registerMethodImplementation(type, "random", Buf_random, 1);
    Type_registerMethodImplementation(type, "read", Buf_read, 0);
    Type_registerMethodImplementation(type, "readAllBuf", Buf_readAllBuf, 0);
    Type_registerMethodImplementation(type, "readAllLines", Buf_readAllLines, 0);
    Type_registerMethodImplementation(type, "readAllStr", Buf_readAllStr_0, 0);
    Type_registerMethodImplementation(type, "readAllStr", Buf_readAllStr, 1);
    Type_registerMethodImplementation(type, "readBool", Buf_readBool, 0);
    Type_registerMethodImplementation(type, "readBuf", Buf_readBuf, 2);
    Type_registerMethodImplementation(type, "readBufFully", Buf_readBufFully, 2);
    Type_registerMethodImplementation(type, "readChar", Buf_readChar, 0);
    Type_registerMethodImplementation(type, "readChars", Buf_readChars, 1);
    Type_registerMethodImplementation(type, "readDecimal", Buf_readDecimal, 0);
    Type_registerMethodImplementation(type, "readF4", Buf_readF4, 0);
    Type_registerMethodImplementation(type, "readF8", Buf_readF8, 0);
    Type_registerMethodImplementation(type, "readLine", Buf_readLine_0, 0);
    Type_registerMethodImplementation(type, "readLine", Buf_readLine, 1);
    Type_registerMethodImplementation(type, "readObj", Buf_readObj_0, 0);
    Type_registerMethodImplementation(type, "readObj", Buf_readObj, 1);
    Type_registerMethodImplementation(type, "readProps", Buf_readProps, 0);
    Type_registerMethodImplementation(type, "readS1", Buf_readS1, 0);
    Type_registerMethodImplementation(type, "readS2", Buf_readS2, 0);
    Type_registerMethodImplementation(type, "readS4", Buf_readS4, 0);
    Type_registerMethodImplementation(type, "readS8", Buf_readS8, 0);
    Type_registerMethodImplementation(type, "readStrToken", Buf_readStrToken_0, 0);
    Type_registerMethodImplementation(type, "readStrToken", Buf_readStrToken_1, 1);
    Type_registerMethodImplementation(type, "readStrToken", Buf_readStrToken, 2);
    Type_registerMethodImplementation(type, "readU1", Buf_readU1, 0);
    Type_registerMethodImplementation(type, "readU2", Buf_readU2, 0);
    Type_registerMethodImplementation(type, "readU4", Buf_readU4, 0);
    Type_registerMethodImplementation(type, "readUtf", Buf_readUtf, 0);
    Type_registerMethodImplementation(type, "remaining", Buf_remaining, 0);
    Type_registerMethodImplementation(type, "seek", Buf_seek, 1);
    Type_registerMethodImplementation(type, "set", Buf_set, 2);
    Type_registerMethodImplementation(type, "toBase64", Buf_toBase64, 0);
    Type_registerMethodImplementation(type, "toDigest", Buf_toDigest, 1);
    Type_registerMethodImplementation(type, "toHex", Buf_toHex, 0);
    Type_registerMethodImplementation(type, "toStr", Buf_toStr, 0);
    Type_registerMethodImplementation(type, "trim", Buf_trim, 0);
    Type_registerMethodImplementation(type, "unread", Buf_unread, 1);
    Type_registerMethodImplementation(type, "unreadChar", Buf_unreadChar, 1);
    Type_registerMethodImplementation(type, "write", Buf_write, 1);
    Type_registerMethodImplementation(type, "writeBool", Buf_writeBool, 1);
    Type_registerMethodImplementation(type, "writeBuf", Buf_writeBuf_1, 1);
    Type_registerMethodImplementation(type, "writeBuf", Buf_writeBuf, 2);
    Type_registerMethodImplementation(type, "writeChar", Buf_writeChar, 1);
    Type_registerMethodImplementation(type, "writeChars", Buf_writeChars_1, 1);
    Type_registerMethodImplementation(type, "writeChars", Buf_writeChars_2, 2);
    Type_registerMethodImplementation(type, "writeChars", Buf_writeChars, 3);
    Type_registerMethodImplementation(type, "writeDecimal", Buf_writeDecimal, 1);
    Type_registerMethodImplementation(type, "writeF4", Buf_writeF4, 1);
    Type_registerMethodImplementation(type, "writeF8", Buf_writeF8, 1);
    Type_registerMethodImplementation(type, "writeI2", Buf_writeI2, 1);
    Type_registerMethodImplementation(type, "writeI4", Buf_writeI4, 1);
    Type_registerMethodImplementation(type, "writeI8", Buf_writeI8, 1);
    Type_registerMethodImplementation(type, "writeObj", Buf_writeObj_1, 1);
    Type_registerMethodImplementation(type, "writeObj", Buf_writeObj, 2);
    Type_registerMethodImplementation(type, "writeProps", Buf_writeProps, 1);
    Type_registerMethodImplementation(type, "writeUtf", Buf_writeUtf, 1);
    Type_registerMethodImplementation(type, "writeXml", Buf_writeXml_1, 1);
    Type_registerMethodImplementation(type, "writeXml", Buf_writeXml, 2);
    Type_registerFieldImplementation(type, "capacity", Buf_capacity, Buf_setCapacity);
    Type_registerFieldImplementation(type, "charset", Buf_charset, Buf_setCharset);
    Type_registerFieldImplementation(type, "endian", Buf_endian, Buf_setEndian);
    Type_registerFieldImplementation(type, "size", Buf_size, Buf_setSize);
    Buf_classType = type;
    Buf_initStatic();
  }
  return Buf_classType;
}


VTable* Buf_initVTable(void)
{
  if (Buf_classVTable == NULL)
  {
    VTable* vtable = memoryAlloc(sizeof(BufVTable));
    memcpy(vtable, Obj_initVTable(), sizeof(FanObjVTable));
    ((FanObjVTable*)vtable)->equals = Buf_equals;
    ((FanObjVTable*)vtable)->toStr = Buf_toStr;
    Buf_classVTable = vtable;
  }
  return Buf_classVTable;
}


void Buf_initStatic(void)
{
  const char base64Chars[64] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
  for (Int i = 0; i < 128; i++)
    kBase64Inv[i] = -1;
  for (Int i = 0; i < 64; i++)
    kBase64Inv[(Int)base64Chars[i]] = i;

  for (Int i = 0; i < 128; i++)
    kHexInv[i] = -1;
  for (Int i = 0; i < 10; i++)
    kHexInv['0' + i] = i;
  for (Int i = 10; i < 16; i++)
    kHexInv['a' + i - 10] = kHexInv['A' + i - 10] = i;
}
